Previous Book Contents Book Index Next

Inside Macintosh: QuickDraw GX Programmer's Overview / Part 2 - The QuickDraw GX Programming Cookbook
Chapter 7 - Handling Typography


Text Editing

This programming recipe allows the user to edit text in a layout shape by pressing a key when there is a layout shape with a selection.

This recipe discusses three types of keypresses: arrow keys, the Delete key,
and character keys. For each type of keypress, this recipe shows how to respond if the current selection is a caret selection and how to respond if
it's a range selection.

Overview of Recipe Steps

The steps in this recipe show you how to:

    1. Determine if the user pressed a key
    2. Handle arrow keys
    3. Handle the Delete key
    4. Handle character keys

You need to follow all of the steps of this recipe to allow your users to edit a layout shape.

Functions Used in This Recipe

QuickDraw GX functions used in this recipe:
GXDrawShape"Shape Objects"
QuickDraw GX Objects
GXGetLeftVisualOffset"Layout Carets, Highlighting, and
Hit-Testing"
QuickDraw GX Typography
GXGetRightVisualOffset"Layout Carets, Highlighting, and
Hit-Testing"
QuickDraw GX Typography
GXSetLayoutParts"Layout Shapes"
QuickDraw GX Typography

Standard Macintosh functions used in this recipe:
WaitNextEvent"Event Manager"
Macintosh Toolbox Essentials

This recipe gives a brief description of these functions; you can find complete reference information for these functions in the Inside Macintosh suite of books.

This recipe also uses functions from the QuickDraw GX libraries:
LockEditHandlelayout edit library
NewSelectionAndHighlightlayout edit library
GetSelectionTypeselection library
GetCaretSelectionselection library
GetPreviousOffsetlayout library

Recipe Step Descriptions

In this section, each step is described individually.

  1. Determine if the user pressed a key

    You can determine if the user of your application has pressed a key by examining the what field of the event record returned by the standard Macintosh function WaitNextEvent. If the value of this field is keyDown or autoKey, you should determine if the user is trying to edit a layout shape.

    As a simple example, you might have a global variable named gCurrentlySelectedLayout, which contains a reference to the currently selected layout if one is selected and nil if not. You could then determine
    if the user is trying to edit a layout shape simply by examining if there
    is a currently selected layout, as with this code:

    if (gCurrentlySelectedLayout != nil) {
    MyHandleLayoutKeyPress(gCurrentLayoutEditHandle,
    theEvent->message & charCodeMask);
    }

    In your MyHandleLayoutKeyPress function, you need to determine what type of key was pressed--for example, an arrow key, the Delete key, or a character key--and respond accordingly.

    Here is the flow of control for the MyHandleLayoutKeyPress function:

    void MyHandleLayoutKeyPress(LayoutEditHandle handle, 
    char key)
    {
    LayoutEditPtr myLayoutEdit = LockEditHandle(handle);

        switch (key) {

            case leftArrow:
    case rightArrow: {
    /* Handle arrow keys. See Step 2. */
    break;
    }

            case backSpace: {
    /* Handle Delete key. See Step 3. */
    break;
    }

            default: {
    /* Handle character keys. See Step 4. */
    break;
    }
    }

    The next few steps, Step 2 through Step 4, show you how to handle each type of keypress.

  2. Handle arrow keys

    If the user pressed an arrow key, you need to

    • erase the current highlight shape
    • update the selection stored in your layout edit shape
    • recalculate the highlight shape
    • draw the new highlight shape

      To erase the current highlight shape, you simply have to draw it again using the GXDrawShape function:

      GXDrawShape(myLayoutEdit->highlight); /* erase previous */

      How you update the selection stored in your layout edit shape depends on whether the user pressed the Left Arrow key or the Right Arrow key. In either case, the new selection is a caret selection.

      If, for instance, the user pressed the Left Arrow key, the new selection is the offset that visually appears one character to the left of the old selection. QuickDraw GX provides the function GXGetLeftVisualOffset for just
      this purpose.

      First, you need to determine the offset that corresponds to the left edge of the current selection. You can retrieve this information from your layout edit structure, thus:

      SelectionOffset oldOffset;

      oldOffset = myLayoutEdit->selectionRanges.ranges[0].minOffset;

      The selectionRanges field of the layout edit structure is an array that contains a ranges field, which is itself an array of selection ranges. Each element in this array contains two fields:

    • The minOffset field specifies the offset at the beginning of a selection.
    • The maxOffset field specifies the offset at the end of a selection.

      Although the selectionRanges field allows you to provide for multiple-
      range selections, the example in this recipe only allows for single-range selections, so only the first element of the array is used.

      Once you've found the offset that corresponds to the left edge of the previous selection, you can determine the offset that visually appears
      one character to the left:

      newOffset = GXGetLeftVisualOffset(myLayoutEdit->layout,
      oldOffset);

      Similarly, if the user pressed the Right Arrow key, you find the offset corresponding to the right edge of the previous selection and then
      calculate the offset that visually appears one character to the right:

      oldOffset = myLayoutEdit->selectionRanges.ranges[0].maxOffset;
      newOffset = GXGetLeftVisualOffset(myLayoutEdit->layout,
      oldOffset);
      }

      Once you've found the new offset, you can call the library function NewSelectionAndHighlight function to store the new selection in your layout edit structure and calculate a new highlight shape. You can finish
      by drawing the highlight:

      NewSelectionAndHighlight(myLayoutEdit, newOffset, newOffset);
      GXDrawShape(myLayoutEdit->highlight);

  3. Handle the Delete key

    If the user pressed the Delete key, you need to determine if the current selection is a caret selection or a range selection. If it's a caret selection, you want to delete the character in the text before the current offset. If it's a range selection, you delete the characters in the current selection.

    You can determine the selection type using the GetSelectionType library function:

    SelectionType selectionType;

    selectionType = GetSelectionType(myLayoutEdit->selection);

    If the value returned by this function is simpleCaret, you need to delete
    the character before the current offset. You can use the library function GetCaretSelection to find the current offset, the library function GetPreviousOffset to find the previous offset, and the QuickDraw GX function GXSetLayoutParts to delete the character:

    SelectionOffset oldOffset;

    oldOffset = GetCaretSelection(myLayoutEdit->selection, nil);

    if (caret > 0) {
    SelectionOffset previousOffset;

        previousOffset = GetPreviousOffset(myLayoutEdit->layout,
    oldOffset);

        GXSetLayoutParts(myLayoutEdit->layout,
    previousOffset,
    oldOffset,
    0, nil, nil,
    0, nil, nil,
    0, nil, nil);

        GXDrawShape(myLayoutEdit->highlight); /* erase old */    
    NewSelectionAndHighlight(myLayoutEdit,
    previousOffset, previousOffset);
    GXDrawShape(myLayoutEdit->highlight); /* draw new */
    }

    If the current selection is a range selection, you need to delete the characters in the selection. You can use this code:

    SelectionOffset rangeStart, rangeEnd;

    rangeStart = myLayoutEdit->selectionRanges.ranges[0].minOffset;
    rangeEnd = myLayoutEdit->selectionRanges.ranges[0].maxOffset;

    GXSetLayoutParts(myLayoutEdit->layout,
    rangeStart,
    rangeEnd,
    0, nil, nil,
    0, nil, nil,
    0, nil, nil);

    GXDrawShape(myLayoutEdit->highlight); /* erase old */NewSelectionAndHighlight(myLayoutEdit, 
    rangeStart, rangeEnd);
    GXDrawShape(myLayoutEdit->highlight); /* draw new */
  4. Handle character keys

    If the user pressed a character key, you need to insert the character into the text of the layout.

    First, however, you need to delete the characters in the current selection if the selection was a range selection, as you did in Step 3:

    SelectionOffset rangeStart, rangeEnd;

    rangeStart = myLayoutEdit->selectionRanges.ranges[0].minOffset;
    rangeEnd = myLayoutEdit->selectionRanges.ranges[0].maxOffset;

    GXSetLayoutParts(myLayoutEdit->layout,
    rangeStart,
    rangeEnd,
    0, nil, nil,
    0, nil, nil,
    0, nil, nil);

    Now you can use the GXSetLayoutParts function to insert the character into the layout:

    SelectionOffset currentOffset;
    char charToInsert[1];

    currOffset = myLayoutEdit->selectionRanges.ranges[0].minOffset;

    charToInsert[1] = key;

    GXSetLayoutParts(myLayoutEdit->layout,
    currentOffset,
    currentOffset,
    1, 1, &charToInsert,
    0, nil, nil,
    0, nil, nil);

    Finally, you can erase the old highlight shape and draw the new one:

    GXDrawShape(myLayoutEdit->highlight); /* erase old */NewSelectionAndHighlight(myLayoutEdit, 
    currentOffset + 1, currentOffset + 1);

Related Recipes

The previous recipe, "Hit-Testing Layout Shapes," shows how you can allow the user to select text in a layout shape by pressing and dragging the mouse.

The recipes in Chapter 4, "Using the QuickDraw GX Environment," show you how to initialize QuickDraw GX. You should read the recipes in that chapter before using any recipes in this chapter.

The recipes in Chapter 5, "Using Macintosh Windows," show you how to use QuickDraw GX with Macintosh windows. You need to be familiar with the information in that chapter before you can display layout shapes in a Macintosh window.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
6 JUL 1996